package com.wyf.test.widget;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.LinearInterpolator;

import com.wyf.test.R;
import com.wyf.test.util.Tools;

import java.lang.ref.WeakReference;

/**
 * 水波纹
 */
public class VoiceSinWaveView extends View {

    private static final String TAG = "VoiceSinWaveView";

    /**
     * 改变相位消息标识
     */
    private static final int CHANGE_PHASE = 0x1001;

    /**
     * 上一次获取的音量值
     */
    private float mLastVolume = 0.0f;

    /**
     * 本次获取的音量值
     */
    private float mVolume = 0.0f;

    /**
     * 上次获得音量值的时间
     */
    private long mLastVolumeTimeMillis = 0;

    /**
     * 主线条画笔
     */
    private Paint mPrimaryPaint;

    /**
     * 次线条画笔
     */
    private Paint mSecondaryPaint;

    /**
     * 填充区域画笔
     */
    private Paint mFillPaint;

    /**
     * 蒙板画笔
     */
    private Paint mMaskPaint;

    /**
     * 主线条 Path
     */
    private Path mPrimaryPath;

    /**
     * 次线条 Path
     */
    private Path mSecondaryPath;

    /**
     * 填充区域 Path
     */
    private Path mFillPath;

    /**
     * 主线条宽度，单位dp
     */
    private float mPrimaryWidth = 1.5f;

    /**
     * 次线条宽度，单位dp
     */
    private float mSecondaryWidth = 1.5f;

    /**
     * 最大振幅
     */
    private float mMaxAmplitude = 0.0f;

    /**
     * 归一化振幅系数
     */
    private float mNormedAmplitude = 0.0f;

    /**
     * 次线条透明度百分比
     */
    private float mSecondaryAlphaRatio = 0.6f;

    /**
     * 填充区域透明度百分比
     */
    private float mFillAlphaRatio = 0.2f;

    /**
     * 画笔透明度最大值
     */
    private static final int MAX_ALPHA_VALUE = 255;

    /**
     * 在 x 轴上的绘制密度（绘制间隔，单位为 dp）
     */
    private int mDensity = 2;

    /**
     * 该 view 的宽度（单位 px）
     */
    private int mWidth;

    /**
     * 该 view 的高度（单位 px）
     */
    private int mHeight;

    /**
     * 主线条频率，用于计算 y 坐标
     */
    private float mPrimaryFrequency = 2.0f;

    /**
     * 主线条频率，用于计算 y 坐标
     */
    private float mSecondaryFrequency = 1.6f;

    /**
     * 主线条相位偏移速度
     */
    private float mPrimaryPhaseShift = -0.20f;

    /**
     * 次线条相位偏移速度
     */
    private float mSecondaryPhaseShift = -0.1994f;

    /**
     * 主线条初始相位
     */
    private float mPrimaryPhase = 0.0f;

    /**
     * 次线条初始相位
     */
    private float mSecondaryPhase = 3.5f;

    /**
     * 音量值属性动画
     */
    private ValueAnimator mValueAnimator;

    /**
     * 淡出效果属性动画
     */
    private ValueAnimator mFadeAnimator;

    /**
     * 如果结束时音量值过大，使波纹逐渐变小的动画时间
     */
    private long mVoiceWaveDecreaseTime = 200;

    /**
     * 淡出动画持续时间
     */
    private long mFadeAnimatorTime = 250;

    /**
     * 是否接收音量值
     */
    private boolean mIsPermitReceiveVolume = false;

    /**
     * 是否发送波纹淡出到1/4的事件消息
     */
    private boolean mHasSendFadeToQuarter = false;

    /**
     * 所在布局View，将占满该布局
     */
    private ViewGroup mParent;

    /**
     * 声波纹 View 回调对象
     */
    private VoiceWaveCallBack mVoiceWaveCallBack;

    /**
     * 记录时间线程
     */
    private TimerThread mTimerThread;

    /**
     * 主线程 handler
     */
    private MainThreadHandler mMainHandler;

    /**
     * 波纹动画位图（波纹淡出动画中使用）
     */
    private Bitmap mSinWaveBitmap;

    /**
     * 波纹动画位图画布（波纹淡出动画中使用）
     */
    private Canvas mSinWaveCanvas;

    /**
     * 波纹淡出动画开始淡出的 x 坐标
     */
    private int mFadeX = 0;

    /**
     * 蒙板与波纹混合模式（波纹淡出动画中使用）
     */
    private PorterDuffXfermode mPorterDuffXfermode;

    /**
     * 线性插值
     */
    private LinearInterpolator mLinearInterpolator;

    /**
     * 是否需要获取该 View 的布局大小
     */
    private boolean mIsNeedGetSize = true;

    public VoiceSinWaveView(Context context) {
        this(context, null, 0);
    }

    public VoiceSinWaveView(Context context, VoiceWaveCallBack voiceWaveCallBack) {
        this(context, null, 0);
        mVoiceWaveCallBack = voiceWaveCallBack;
    }

    public VoiceSinWaveView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mMainHandler = new MainThreadHandler(this);
        initialize();
    }

    private static class MainThreadHandler extends Handler {

        private WeakReference<VoiceSinWaveView> mWeakVoiceSinWaveView;

        public MainThreadHandler(VoiceSinWaveView voiceSinWaveView) {
            mWeakVoiceSinWaveView = new WeakReference<VoiceSinWaveView>(voiceSinWaveView);
        }

        @Override
        public void handleMessage(Message msg) {
            if (msg.what == CHANGE_PHASE) {
                VoiceSinWaveView voiceSinWaveView = mWeakVoiceSinWaveView.get();
                if (voiceSinWaveView != null) {
                    voiceSinWaveView.drawFrame();
                    voiceSinWaveView.invalidate();
                    voiceSinWaveView.changePhase();
                }

            }
        }
    }

    /**
     * 设置回调对象
     */
    public void setCallBack(VoiceWaveCallBack voiceWaveCallBack) {
        mVoiceWaveCallBack = voiceWaveCallBack;
    }

    /**
     * 初始化画笔以及 Path
     */
    private void initialize() {

        // 初始化主线条画笔
        mPrimaryPaint = new Paint();
        mPrimaryPaint.setAntiAlias(true);
        mPrimaryPaint.setStyle(Paint.Style.STROKE);
        mPrimaryPaint.setStrokeWidth(Tools.dip2px(getContext(), mPrimaryWidth));

        // 初始化次线条画笔
        mSecondaryPaint = new Paint();
        mSecondaryPaint.setAntiAlias(true);
        mSecondaryPaint.setStyle(Paint.Style.STROKE);
        mSecondaryPaint.setStrokeWidth(Tools.dip2px(getContext(), mSecondaryWidth));
        mSecondaryPaint.setAlpha((int) (mSecondaryAlphaRatio * MAX_ALPHA_VALUE));

        // 初始化填充区域画笔
        mFillPaint = new Paint();
        mFillPaint.setAlpha((int) (mFillAlphaRatio * MAX_ALPHA_VALUE));

        // 初始化蒙板画笔
        mMaskPaint = new Paint();

        // 主线条，次线条，填充区域对应的 Path
        mPrimaryPath = new Path();
        mSecondaryPath = new Path();
        mFillPath = new Path();

        mPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
        mLinearInterpolator = new LinearInterpolator();

    }

    private void setPaintShader() {
        // 获得父容器宽高（ 此时获取，是因为设置画笔颜色渐变效果时就要用到 ）
        mWidth = mParent.getWidth();
        mHeight = mParent.getHeight();

        mSinWaveBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
        mSinWaveCanvas = new Canvas(mSinWaveBitmap);

        // 设置最大振幅
        mMaxAmplitude = (mHeight - 4.0f) * 0.5f;

        // 设置主线条画笔颜色渐变
        int primaryStartColor = getResources().getColor(R.color.duer_voice_primary_start);
        int primaryEndColor = getResources().getColor(R.color.duer_voice_primary_end);
        LinearGradient primaryPaintLG = new LinearGradient(0, 0, mWidth, 0,
                primaryStartColor, primaryEndColor, Shader.TileMode.MIRROR);
        mPrimaryPaint.setShader(primaryPaintLG);

        // 设置次线条画笔颜色渐变
        int secondaryStartColor = getResources().getColor(R.color.duer_voice_secondary_start);
        int secondaryEndColor = getResources().getColor(R.color.duer_voice_secondary_end);
        LinearGradient secondaryPaintLG = new LinearGradient(0, 0, mWidth, 0,
                secondaryStartColor, secondaryEndColor, Shader.TileMode.MIRROR);
        mSecondaryPaint.setShader(secondaryPaintLG);

        // 设置填充区域画笔颜色渐变
        int fillTopColor = getResources().getColor(R.color.duer_voice_fill_top);
        int fillBottomColor = getResources().getColor(R.color.duer_voice_fill_bottom);
        LinearGradient fillPaintLG = new LinearGradient(0, mHeight / 2 - mMaxAmplitude, 0,
                mHeight / 2 + mMaxAmplitude, fillTopColor, fillBottomColor, Shader.TileMode.MIRROR);
        mFillPaint.setShader(fillPaintLG);
    }

    /**
     * 在父 View 中添加该波纹View
     */
    public void showInParentView(ViewGroup parent) {

        if (parent == null) {
            return;
        }
        if (mParent != null) {
            return;
        }

        mIsNeedGetSize = true;

//        if (mWidth != 0) {
//            mSinWaveBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
//            mSinWaveCanvas = new Canvas(mSinWaveBitmap);
//        }

        parent.addView(this, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        mParent = parent;
    }

    /**
     * 在父 View 中移除该波纹View
     */
    public void removeFromParent() {

        if (mParent == null) {
            return;
        }

        mParent.removeView(this);
        mParent = null;

        cancel();
    }

    /**
     * 取消动画
     */
    private void cancel() {
//        if (mIsPermitReceiveVolume == true) {
//            return;
//        }

        // 停止音量值属性动画
        if (mValueAnimator != null) {
            mValueAnimator.cancel();
            mValueAnimator = null;
        }

        // 停止淡出动画
        if (mFadeAnimator != null) {
            mFadeAnimator.cancel();
            mFadeAnimator = null;
        }
    }

    /**
     * 开始接收音量值
     */
    public void start() {
        if (mIsPermitReceiveVolume == true) {
            return;
        }
        mIsPermitReceiveVolume = true;
        mHasSendFadeToQuarter = false;

        mTimerThread = new TimerThread(this);
        mTimerThread.start();
    }

    /**
     * 停止接收音量值，并绘制淡出动画
     */
    public void stop() {

        if (mIsPermitReceiveVolume == false) {
            return;
        }
        mIsPermitReceiveVolume = false;

        // 停止音量值属性动画
        if (mValueAnimator != null) {
            mValueAnimator.cancel();
            mValueAnimator = null;
        }

        // 如果淡出动画时音量值比较大，使音量值慢慢减少到10，来显示波纹效果
        if (mVolume > 10) {
            // 使用属性动画演示由上次音量值到本次音量值的声波纹动效
            mValueAnimator = ValueAnimator.ofFloat(mVolume, 10);
            mValueAnimator.setDuration(mVoiceWaveDecreaseTime);
            mValueAnimator.setInterpolator(mLinearInterpolator);
            mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mVolume = (Float) animation.getAnimatedValue();
                }
            });

            mValueAnimator.start();
        }

        // 设置蒙板颜色对应的透明效果颜色
        final int[] colors = {0x00000000, 0xFF000000, 0xFF000000, 0xFF000000, 0x00000000};
        final float[] positions = {0, 0.2f, 0.5f, 0.8f, 1};

        // 使用属性动画来改变透明区域的大小，本质由蒙板画笔的渐变效果实现
        mFadeAnimator = ValueAnimator.ofInt(0, mWidth / 2);
        mFadeAnimator.setDuration(mFadeAnimatorTime);
        mFadeAnimator.setInterpolator(new AccelerateInterpolator());
        mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mFadeX = (int) animation.getAnimatedValue();

                // 待波纹淡出至大约 1/4 时通知回调对象
                if ((!mHasSendFadeToQuarter) && (mFadeX > mWidth * 5 / 14)) {
                    mVoiceWaveCallBack.fadeToQuarter();
                    mHasSendFadeToQuarter = true;
                }

                LinearGradient maskPaintLG = new LinearGradient(mFadeX, 0, mWidth - mFadeX, 0, colors, positions,
                        Shader.TileMode.MIRROR);
                mMaskPaint.setShader(maskPaintLG);
            }
        });

        // 淡出动画添加监听，监听到淡出动画结束时通知回调对象
        mFadeAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                mVoiceWaveCallBack.fadeOut();
            }
        });
        mFadeAnimator.start();
    }

    /**
     * 改变当前音量值
     */
    public void changeVolume(float volume) {

        if (!mIsPermitReceiveVolume) {
            return;
        }

        if ((volume < 0) || (volume > 100)) {
            return;
        }

        long currentTime = System.currentTimeMillis();

        // 首次设置音量值，设置 duration = 100ms，即100ms由直线变换到该音量值对应的效果
        if (0 == mLastVolumeTimeMillis) {
            mLastVolumeTimeMillis = currentTime - 100;
        }

        long duration = currentTime - mLastVolumeTimeMillis;

        if (duration <= 0) {
            duration = 100;
        }

        // 获得新的音量值时，停止上次动画
        if (mValueAnimator != null) {
            mValueAnimator.cancel();
            mValueAnimator = null;
        }

        // 使用属性动画演示由上次音量值到本次音量值的声波纹动效
        mValueAnimator = ValueAnimator.ofFloat(mLastVolume, volume);
        mValueAnimator.setDuration(duration);
        mValueAnimator.setInterpolator(mLinearInterpolator);
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mVolume = (Float) animation.getAnimatedValue();
                mLastVolume = mVolume;
            }
        });

        mValueAnimator.start();
        mLastVolumeTimeMillis = currentTime;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mIsNeedGetSize) {
            setPaintShader();
            mIsNeedGetSize = false;
        }

        if (mIsPermitReceiveVolume) {
            canvas.drawColor(Color.TRANSPARENT);

            canvas.drawPath(mFillPath, mFillPaint);
            canvas.drawPath(mSecondaryPath, mSecondaryPaint);
            canvas.drawPath(mPrimaryPath, mPrimaryPaint);
        } else {
            // 创建一个图层，在图层上演示图形混合后的效果
            int sc = canvas.saveLayer(0, 0, mWidth, mHeight, null, Canvas.MATRIX_SAVE_FLAG
                    | Canvas.CLIP_SAVE_FLAG
                    | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG
                    | Canvas.FULL_COLOR_LAYER_SAVE_FLAG
                    | Canvas.CLIP_TO_LAYER_SAVE_FLAG);

            canvas.drawRect(mFadeX, 0, mWidth - mFadeX, mHeight, mMaskPaint);

            mMaskPaint.setXfermode(mPorterDuffXfermode);

            mSinWaveCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
            mSinWaveCanvas.drawPath(mFillPath, mFillPaint);
            mSinWaveCanvas.drawPath(mSecondaryPath, mSecondaryPaint);
            mSinWaveCanvas.drawPath(mPrimaryPath, mPrimaryPaint);

            canvas.drawBitmap(mSinWaveBitmap, 0, 0, mMaskPaint);

            mMaskPaint.setXfermode(null);
            canvas.restoreToCount(sc);
        }

    }

    /**
     * 改变相位，实现波纹右移动效果
     */
    private void changePhase() {
        mPrimaryPhase += mPrimaryPhaseShift;
        mSecondaryPhase += mSecondaryPhaseShift;
        if (mPrimaryPhase < 1.0f - Float.MAX_VALUE) {
            mPrimaryPhase = 0.0f;
            mSecondaryPhase = 3.5f;
        }
    }

    /**
     * 绘制一帧
     */
    private void drawFrame() {

        if (mIsNeedGetSize) {
            return;
        }
        // 由音量值大小对应到 0 到 1 的归一化系数，控制声波纹振幅高低
//        mNormedAmplitude = (float) (Math.log(mVolume + 1) / Math.log(100.0f)) * 0.8f;
        mNormedAmplitude = mVolume / 100.0f * 0.8f;
        mNormedAmplitude = Math.max(0.05f, mNormedAmplitude);
        // 主线条，次线条，填充区域对应的 Path
        mPrimaryPath.rewind();
        mSecondaryPath.rewind();
        mFillPath.rewind();

        // 生成主线条 Path 以及填充区域 Path 的主线条部分
        float y = getY(0, mWidth, mHeight, mMaxAmplitude, mNormedAmplitude,
                mPrimaryFrequency, mPrimaryPhase);
        mPrimaryPath.moveTo(0, y);
        for (int x = 1; x <= mWidth; x += Tools.dip2px(getContext(), mDensity)) {
            y = getY(x, mWidth, mHeight, mMaxAmplitude, mNormedAmplitude,
                    mPrimaryFrequency, mPrimaryPhase);
            mPrimaryPath.lineTo(x, y);
        }
        y = getY(mWidth, mWidth, mHeight, mMaxAmplitude, mNormedAmplitude, mPrimaryFrequency, mPrimaryPhase);
        mPrimaryPath.lineTo(mWidth, y);

        // 生成次线条 Path 以及填充区域 Path 的次线条部分
        y = getY(mWidth, mWidth, mHeight, mMaxAmplitude, mNormedAmplitude * 0.8f,
                mSecondaryFrequency, mSecondaryPhase);
        mSecondaryPath.moveTo(mWidth, y);
        for (int x = mWidth - 1; x >= 0; x -= Tools.dip2px(getContext(), mDensity)) {
            y = getY(x, mWidth, mHeight, mMaxAmplitude, mNormedAmplitude * 0.8f,
                    mSecondaryFrequency, mSecondaryPhase);
            mSecondaryPath.lineTo(x, y);
        }
        y = getY(0, mWidth, mHeight, mMaxAmplitude, mNormedAmplitude * 0.8f, mSecondaryFrequency, mSecondaryPhase);
        mSecondaryPath.lineTo(0, y);

        mFillPath.addPath(mPrimaryPath);
        mFillPath.addPath(mSecondaryPath);
    }

    /**
     * 根据 x 轴坐标绘制 y 轴坐标
     */
    private float getY (int x, float width, float height, float maxAmplitude, float normedAmplitude,
                        float frequency, float phase) {
        float scaling = 1.0f - (float) Math.pow(2 * x / width - 1, 2);
        return scaling * maxAmplitude * normedAmplitude * (float) Math.sin(2 * Math.PI
                * (x / width) * frequency + phase) + height * 0.5f;
    }

    /**
     * 重置绘制相关参数
     */
    private void reset() {
        mVolume = 0.0f;
        mLastVolume = 0.0f;
        mLastVolumeTimeMillis = 0L;
        mPrimaryPhase = 0.0f;
        mSecondaryPhase = 3.5f;
        mIsPermitReceiveVolume = false;
        mHasSendFadeToQuarter = false;
        mFadeX = 0;
        if (mSinWaveBitmap != null) {
            mSinWaveBitmap.recycle();
            mSinWaveBitmap = null;
            mSinWaveCanvas = null;
        }
    }

    /**
     * 声波纹 View 回调接口
     */
    public interface VoiceWaveCallBack {

        // 声波纹淡出至大约 1/4
        void fadeToQuarter();
        // 声波纹完全淡出
        void fadeOut();
    }

    private static class TimerThread extends Thread {
        // 该线程是否取消标志
        private boolean isCancel = false;
        private WeakReference<VoiceSinWaveView> mWeakVoiceSinWaveView;

        public TimerThread(VoiceSinWaveView voiceSinWaveView) {
            mWeakVoiceSinWaveView = new WeakReference<VoiceSinWaveView>(voiceSinWaveView);
        }

        public synchronized void setCancel(boolean cancel) {
            isCancel = cancel;
        }

        public synchronized boolean getCancel() {
            return isCancel;
        }

        @Override
        public void run() {
            while (!getCancel()) {
                VoiceSinWaveView voiceSinWaveView = mWeakVoiceSinWaveView.get();
                if (voiceSinWaveView != null) {
                    voiceSinWaveView.mMainHandler.sendEmptyMessage(CHANGE_PHASE);
                    try {
                        Thread.sleep(16);

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (null != mTimerThread) {
            mTimerThread.setCancel(true);
        }
        if (null != mMainHandler) {
            mMainHandler.removeMessages(CHANGE_PHASE);
        }
        reset();
    }
}